<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, user-scalable=no"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <meta name="renderer" content="webkit">
    <meta http-equiv="Cache-Control" content="no-siteapp"/>
    <link rel="preconnect" href="//cdn.w3cbus.com">
    <link rel="preconnect" href="//cdn.jsdelivr.net">
    <title>HServer-APM</title>
    <link
            rel="stylesheet"
            href="/css/mdui.min.css"
    />
    <script
            src="/js/mdui.min.js"
    ></script>
    <script>var $ = mdui.$;</script>
    <script src="/js/vue.js"></script>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="/css/index.css">
    <!-- 引入组件库 -->
    <script src="/js/index.js"></script>
    <script type="text/javascript" src="/js/echarts.min.js"></script>

    <style>
        #app{
            margin: 10px;
        }
        .el-table .warning-row {
            background: oldlace;
        }

        .el-table .success-row {
            background: #f0f9eb;
        }

        .box-card {
            margin-top: 10px;
        }
    </style>

</head>
<body class="mdui-drawer-body-left mdui-appbar-with-toolbar  mdui-theme-primary-indigo mdui-theme-accent-pink mdui-theme-layout-auto">
<header class="mdui-appbar mdui-appbar-fixed">
    <div class="mdui-toolbar mdui-color-theme">
        <span class="mdui-btn mdui-btn-icon mdui-ripple mdui-ripple-white"
              mdui-drawer="{target: '#main-drawer', swipe: true}"><i class="mdui-icon material-icons">menu</i></span>
        <a href="index.html" class="mdui-typo-headline mdui-hidden-xs">HServer-APM</a>
    </div>
</header>

<div class="mdui-drawer" id="main-drawer">
    <div class="mdui-list" mdui-collapse="{accordion: true}" style="margin-bottom: 76px;">
        <div class="mdui-collapse-item ">
            <div class="mdui-collapse-item-header mdui-list-item">
                <i class="mdui-list-item-icon mdui-icon material-icons mdui-text-color-blue">near_me</i>
                <div class="mdui-list-item-content">
                    <a href="index.html">服务大盘</a>
                </div>
            </div>
        </div>
        <div class="mdui-collapse-item ">
            <div class="mdui-collapse-item-header mdui-list-item mdui-ripple">
                <i class="mdui-list-item-icon mdui-icon material-icons mdui-text-color-deep-orange">layers</i>
                <div class="mdui-list-item-content"><a href="request.html">请求记录</a></div>
            </div>
        </div>
        <div class="mdui-collapse-item ">
            <div class="mdui-collapse-item-header mdui-list-item mdui-ripple">
                <i class="mdui-list-item-icon mdui-icon material-icons mdui-text-color-green">widgets</i>
                <div class="mdui-list-item-content"><a href="log.html">请求日志</a></div>
            </div>
        </div>
        <div class="mdui-collapse-item ">
            <div class="mdui-collapse-item-header mdui-list-item mdui-ripple">
                <i class="mdui-list-item-icon mdui-icon material-icons mdui-text-color-purple">local_mall</i>
                <div class="mdui-list-item-content"><a href="config.html">APM配置</a></div>
            </div>
        </div>
    </div>
</div>

<div class="mdui-panel" mdui-panel>
    <div id="app">

        <div>
            <el-form :inline="true" :model="query" class="demo-form-inline">
                <el-form-item label="应用名">
                    <el-input v-model="query.appName"></el-input>
                </el-form-item>
                <el-form-item label="链ID">
                    <el-input v-model="query.chainId"></el-input>
                </el-form-item>
                <el-form-item label="日级别">
                    <el-select v-model="query.level" placeholder="请选择日志级别">
                        <el-option label="全部" value=""></el-option>
                        <el-option label="DEBUG" value="DEBUG"></el-option>
                        <el-option label="INFO" value="INFO"></el-option>
                        <el-option label="WARN" value="WARN"></el-option>
                        <el-option label="ERROR" value="ERROR"></el-option>
                    </el-select>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="getLogInfo(true)">查询</el-button>
                </el-form-item>
            </el-form>

        </div>

        <el-table
                :data="tableData"
                style="width: 100%">
            <el-table-column type="expand">
                <template slot-scope="props">

                    <el-card class="box-card" v-if="props.row.message">
                        <div slot="header" class="clearfix">
                            <span>日志信息</span>
                        </div>
                        <div class="text item">
                            {{ props.row.message }}
                        </div>
                    </el-card>

                    <el-card class="box-card" v-if="props.row.stackTrace">
                        <div slot="header" class="clearfix">
                            <span>调用栈</span>
                        </div>
                        <div class="text item" v-html="props.row.stackTrace.replaceAll('->','<br>')">
                        </div>
                    </el-card>

                </template>
            </el-table-column>
            <el-table-column
                    label="应用名"
                    prop="appName">
            </el-table-column>
            <el-table-column
                    label="链ID"
                    prop="chainId">
            </el-table-column>
            <el-table-column
                    label="日志级别"
                    prop="level">
            </el-table-column>
            <el-table-column
                    label="线程名"
                    prop="threadName">
            </el-table-column>

            <el-table-column label="打印时间">
                <template slot-scope="scope">
                    <span>{{getTime(scope.row.time)}}</span>
                </template>
            </el-table-column>
            <el-table-column
                    fixed="right"
                    label="操作"
                    width="200">
                <template slot-scope="scope">
                    <el-button @click="viewStack(scope.row,true)" type="text" size="small">栈顺序图</el-button>
                    <el-button @click="viewStack(scope.row,false)" type="text" size="small">关系图</el-button>
                </template>
            </el-table-column>
        </el-table>

        <div class="block">
            <el-pagination
                    :current-page="currentPage"
                    :page-sizes="[2,10,50,100]"
                    :page-size="pageSize"
                    layout="total, sizes, prev, pager, next, jumper"
                    :total="total"
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
            />
        </div>

        <el-dialog title="顺序图" :visible.sync="dialogTableVisible">
            <el-timeline>
                <el-timeline-item
                        v-for="(activity, index) in activities"
                        :key="index">
                    <div>
                        <div>
                            {{activity.content}}
                        </div>
                        <div class="el-timeline-item__timestamp is-bottom" v-if="activity.parameterType">
                            入参类型：{{activity.parameterType}}
                        </div>
                        <div class="el-timeline-item__timestamp is-bottom" v-if="activity.returnType">
                            返回类型：{{activity.returnType}}
                        </div>
                        <div class="el-timeline-item__timestamp is-bottom">
                            耗时：{{activity.ms}}/ms
                        </div>
                        <div class="el-timeline-item__timestamp is-bottom">
                            调用时间：{{activity.time}}
                        </div>
                    </div>
                </el-timeline-item>
            </el-timeline>
        </el-dialog>
        <el-dialog :visible.sync="dialogTableVisible1">
            <div style="height: 70vh;width: 50vw" id="myChats"></div>
        </el-dialog>
    </div>
</div>

<script>
    new Vue({
        el: '#app',
        data() {
            return {
                activities: [],
                dialogTableVisible: false,
                dialogTableVisible1: false,
                query:{
                    appName:'',
                    level:'',
                    chainId:''
                },
                total: 0,
                pageSize: 10,
                currentPage: 1,
                tableData: []
            }
        },
        mounted: function () {
            const that = this;
            that.getLogInfo();
        },
        methods: {
            compare(property) {
                return function (a, b) {
                    var value1 = a[property];
                    var value2 = b[property];
                    return value1 - value2;
                };
            },
            loadChat(datas) {
                const that=this
                const axisData = datas.map(function (item, i) {
                    return item.method;
                });

                const data = datas.map(function (item, i) {
                    return item.ms;
                });

                const links = [];

                datas.map((item, i) => {
                    datas.map((item1, i1) => {
                        if (item.spanId === item1.pspanId) {
                            links.push({
                                source: i1,
                                target: i
                            });
                        }
                    });
                });

                let option = {
                    title: {
                        text: '链路追踪图'
                    },
                    tooltip: {
                        trigger: 'item', //item数据项图形触发，主要在散点图，饼图等无类目轴的图表中使用。
                        axisPointer: {
                            // 坐标轴指示器，坐标轴触发有效
                            type: 'shadow' // 默认为直线，可选为：'line' | 'shadow'
                        },
                        formatter:function (params) {
                            if (params.dataType==="node"){
                                let d=datas[params.dataIndex]
                                return  '  <div>\n' +
                                    '                        <div>\n' +
                                    '                           '+d.className+"."+d.method+'\n' +
                                    '                        </div>\n' +
                                    '                        <div class="el-timeline-item__timestamp is-bottom" v-if="activity.parameterType">\n' +
                                    '                            入参类型：' +
                                    '                           '+d.parameterType+'\n' +
                                    '                        </div>\n' +
                                    '                        <div class="el-timeline-item__timestamp is-bottom" v-if="activity.returnType">\n' +
                                    '                            返回类型：' +
                                    '                           '+d.returnType+'\n' +
                                    '                        </div>\n' +
                                    '                        <div class="el-timeline-item__timestamp is-bottom">\n' +
                                    '                            耗时：' +
                                    '                           '+d.ms+'/ms\n' +
                                    '                        </div>\n' +
                                    '                        <div class="el-timeline-item__timestamp is-bottom">\n' +
                                    '                            调用时间：' +
                                    '                           '+that.getTime(d.time)+'\n' +
                                    '                        </div>\n' +
                                    '                    </div>'
                            }else if (params.dataType==="edge"){
                                let a=datas[params.data.source]
                                let b=datas[params.data.target]

                                return  a.className+"."+a.method+'=>'+b.className+"."+b.method

                            }
                        }

                    },
                    xAxis: {

                        type: 'value',
                        name: "单位（ms）",
                    },
                    yAxis: {
                        type: 'category',
                        boundaryGap: false,
                        data: axisData,
                        name: "方法名",
                    },
                    series: [
                        {
                            type: 'graph',
                            layout: 'none',
                            draggable: true,
                            coordinateSystem: 'cartesian2d',
                            label: {
                                show: true
                            },
                            edgeSymbol: ['circle', 'arrow'],
                            edgeSymbolSize: [4, 10],
                            data: data,
                            links: links,
                            lineStyle: {
                                color: '#2f4554'
                            }
                        }
                    ]
                };
                let dom = document.getElementById("myChats");
                let myChart = echarts.init(dom);
                if (option && typeof option === 'object') {
                    myChart.setOption(option);
                }

            },



            viewStack(val, flag) {
                const that = this;
                $.ajax({
                    type: "GET",
                    url: "/api/linkStack",
                    data: {
                        chainId: val.chainId
                    },
                    dataType: "json",
                    success: function (res) {
                        if (res.code === 200) {
                            res.data=res.data.sort(that.compare('level'));

                            let tmp = [];
                            let all = [];

                            for(let i=0;i<res.data.length;i++){
                                let a = res.data[i]
                                tmp.push(a);
                                if (a.spanId === '-1'||i===res.data.length-1) {
                                    tmp.reverse();
                                    all.push(...tmp);
                                    tmp = [];
                                }
                            }

                            res.data = all.reverse();


                            if (flag) {
                                that.dialogTableVisible = true
                                let dataInfos = []
                                for (let item of res.data) {
                                    dataInfos.push({
                                        content: item.className + "." + item.method,
                                        parameterType: item.parameterType,
                                        ms: item.ms,
                                        returnType: item.returnType,
                                        time: that.getTime(item.time),
                                    })
                                }
                                that.activities = dataInfos.reverse();
                            } else {
                                that.dialogTableVisible1 = true
                                that.$nextTick(function () {
                                    that.loadChat(res.data)
                                })
                            }
                        }
                    }
                });
            },
            getTime(n) {
                var D = new Date(n);
                var year = D.getFullYear();//四位数年份
                var month = D.getMonth() + 1;//月份(0-11),0为一月份
                month = month < 10 ? ('0' + month) : month;
                var day = D.getDate();//月的某一天(1-31)
                day = day < 10 ? ('0' + day) : day;
                var hours = D.getHours();//小时(0-23)
                hours = hours < 10 ? ('0' + hours) : hours;
                var minutes = D.getMinutes();//分钟(0-59)
                minutes = minutes < 10 ? ('0' + minutes) : minutes;
                var s = D.getSeconds();
                s = s < 10 ? ('0' + s) : s;
                var now_time = year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ":" + s;
                return now_time;
            },
            getLogInfo(val) {
                if (val){
                    this.currentPage=1;
                }
                const that = this;
                $.ajax({
                    type: "GET",
                    url: "/api/logInfo",
                    data: {
                        page: that.currentPage,
                        pageSize: that.pageSize,
                        appName:that.query.appName,
                        level: that.query.level,
                        chainId: that.query.chainId
                    },
                    dataType: "json",
                    success: function (res) {
                        if (res.code === 200) {
                            that.tableData = res.data.list
                            that.total = res.data.totalRow
                        }
                    }
                });
            },
            handleCurrentChange(val) {
                this.currentPage = val
                this.getLogInfo()
            },
            handleSizeChange(val) {
                this.pageSize = val
                this.getLogInfo()
            },
        }
    })
</script>
